/* ObjectType.js
 *
 * Copyright 2006, Tim Dwelle.
 *
 * Licensed under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in
 * compliance with the License.  You may obtain a copy of
 * the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied.  See
 * the License for the specific language governing
 * permissions and limitations under the License.
 *
 */

$package("dowry.datatype");

/**
 * Object representation of an object datatype.
 *
 */
$class("ObjectType").$extends("Datatype").$as(
{
    /**
     * Constant for the prefix to place before the entity
     * name, when defining the type.
     *
     */
    ENTITY_PREFIX: Dowry.msgs.getMessage("entity_"),

    /**
     * Property indicating the string name of the field on
     * the object where the value goes.  The default is
     * 'id'.
     *
     */
    key : "id",

    /**
     * Property indicating the datatype of the key's value.
     *
     */
    _keyDatatype : null,

    /**
     * Compares two values for order, based on the
     * comparison rules for this datatype. Returns a
     * negative integer, zero, or a positive integer
     * indicating that the first value is less than, equal
     * to, or greater than the second.
     *
     * <p>
     * In ObjectType, this comparison is based on the
     * compare of the key's datatype, if available.
     * </p>
     *
     * @param value1  the first value to be compared
     *
     * @param value2  the second value to be compared
     *
     * @return        a negative integer, zero, or a
     *                positive integer indicating that
     *                the first value is less than, equal
     *                to, or greater than the second.
     *
     */
    compare : function(value1, value2)
    {
        var rc = 0;

        // compare using the key's datatype
        var dt = this.getKeyDatatype();
        if (dt != null)
        {
            rc = dt.compare(value1, value2);
        }

        // no datatype for the key, just use
        // super.compare()
        else
        {
            rc = this.$super(arguments);
        }

        return rc;
    },

    /**
     * Determines the name of the entity based on the type,
     * because an entity should follow the naming convention
     * of: <code>entity_[entityName]</code>.
     *
     * @return  the entity name, if we can derive it from
     *          the type; otherwise, null
     *
     */
    getEntityName : function()
    {
        var entityName = this.type;

        if (typeof entityName == "string" &&
            entityName.indexOf(this.ENTITY_PREFIX) == 0)
        {
            entityName = entityName.substring(this.ENTITY_PREFIX.length,
                                              entityName.length);
        }
        else
        {
            entityName = null;
        }

        return entityName;
    },

    /**
     * Get the datatype mapped to the key.
     *
     * @return the datatype object for the key
     *
     */
    getKeyDatatype : function()
    {
        var dt = this._keyDatatype;
        if (dt == null)
        {
            var entityName = this.getEntityName();

            // ok, this is a bit hackey, but... we know the
            // name of the entity (from this.type) and we know
            // the name of the key now... so we can determine
            // the datatype of this key by consulting Dowry's
            // entities mapping
            if (entityName != null &&
                Dowry.entities != null &&
                Dowry.entities[entityName] != null &&
                Dowry.entities[entityName][this.key] != null)
            {
                var cfg = Dowry.entities[entityName][this.key];

                if (cfg.type != null &&
                    Dowry.datatypes[cfg.type] != null)
                {
                    var datatypeClass = Dowry.datatypes[cfg.type];
                    datatypeClass = $classLoader.loadClass(datatypeClass);

                    // construct our key datatype instance...
                    var dt = new datatypeClass;
                    Dowry.configure(dt, cfg);

                    this._keyDatatype = dt;
                }
            }
        }

        return dt;
    },

    /**
     * Extends Datatype's <code>toConfiguration()</code> to
     * return the configuration of the key datatype.
     *
     * @return       an object containing the datatype
     *               information as a map
     *
     */
    toConfiguration : function(id, val)
    {
        var cfg = null;

        var dt = this.getKeyDatatype();
        if (dt != null)
        {
            cfg = dt.toConfiguration(id, val);
        }
        else
        {
            cfg = this.$super(arguments);
        }

        return cfg;
    },

    /**
     * Converts some raw value to a value of the appropriate
     * datatype.
     *
     * @param val  the original, raw value representation
     *
     * @return     the value converted to the appropriate
     *             datatype
     *
     */
    toDatatype : function(val)
    {
        // make sure we aren't already being passed
        // in an object of the appropriate datatype
        if (val == null ||
            this.key == null ||
            val[this.key] == null)
        {
            // convert using the key's datatype
            var dt = this.getKeyDatatype();
            if (dt != null)
            {
                val = dt.toDatatype(val);
            }

            // no datatype for the key, just use
            // super.toDatatype()
            else
            {
                val = this.$super(arguments);
            }

            // put the value in an object, using
            // the specified key
            if (val != null)
            {
                var obj = new Object();
                obj[this.key] = val;
                val = obj;
            }
        }

        return val;
    },

    /**
     * Converts some value of the appropriate datatype to
     * a string representation.
     *
     * @param val  the original value of the appropriate
     *             datatype
     *
     * @return     the value converted to a string
     *
     */
    toFormattedString : function(val)
    {
        var s = null;

        // get the key's value
        if (val != null)
        {
            val = val[this.key];
        }

        // convert using the key's datatype
        var dt = this.getKeyDatatype();
        if (dt != null)
        {
            s = dt.toFormattedString(val);
        }

        // no datatype for the key, just use
        // super.toFormattedString() for the provided
        // object's key field
        else
        {
            s = this.$super(arguments);
        }

        return s;
    },

    /**
     * Validates that:
     *
     * <ol>
     *  <li>  the value is provided, if required
     *  </li>
     *  <li>  the value is compatible with the datatype of
     *        the key
     *  </li>
     * </ol>
     *
     * @param val    a value representation to be validated
     *
     * @return       the string error message, if the
     *               input is invalid; otherwise, null
     *
     */
    validate : function(val)
    {
        var msg = null;

        // validate using the key's datatype
        var dt = this.getKeyDatatype();
        if (val != null &&
            dt != null &&
            this.key != null)
        {
            msg = dt.validate(val[this.key]);
        }

        // no datatype for the key, just use
        // super.validate()
        else
        {
            msg = this.$super(arguments);
        }

        return msg;
    }
});